Given are 1344 ETFs from the US. As shown below, the data consists of the Variables Date, Open, High, Low, Close, Volume and OpenInt. The Variable Price has been added for every Date by taking the mean of the High and Low for every date.
## Date Open High Low Close Volume OpenInt Price
## 1: 2010-07-21 24.333 24.333 23.946 23.946 43321 0 24.1395
## 2: 2010-07-22 24.644 24.644 24.362 24.487 18031 0 24.5030
## 3: 2010-07-23 24.759 24.759 24.314 24.507 8897 0 24.5365
## 4: 2010-07-26 24.624 24.624 24.449 24.595 19443 0 24.5365
## 5: 2010-07-27 24.477 24.517 24.431 24.517 8456 0 24.4740
## 6: 2010-07-28 24.477 24.517 24.352 24.431 4967 0 24.4345
overall_return <- function(x){
o.return <- (x[which.max(Date)]$Price - x[which.min(Date)]$Price)/x[which.min(Date)]$Price
o.return
}
overall_returns <- sapply(etfs.red, function(x) overall_return(x))
With the price given for each ETF, I calculated the fractal dimension first introduced by Benoit Mandelbrot. The fractal dimension can be thought of as a measure of roughness for a given geometric object (including curves). To calculate the fractal dimension for finencial price charts various methods have been suggested. Some of them can get quite complicated. I choosed a rather simple one by John Ehlers ( Original publication). The Formula to calculate the fractal dimension \(D\) is as follows:
\[\begin{aligned} D &= \frac{Log(HL1 + HL2) - Log(HL)}{Log(2)} \\ HL1 &= \frac{Max(High, \frac{1}{2}N..N)-Min(Low,\frac{1}{2}N..N)}{\frac{1}{2}N} \\ HL2 &= \frac{Max(High, \frac{1}{2}N)-Min(Low,\frac{1}{2}N)}{\frac{1}{2}N} \\ HL &= \frac{(Max(High,N) - Min(Low,N))}{N} \end{aligned}\]
I applied this formula on all ETFs with 1000 or more data points.
#Remove etfs with very low data
etf.length <- sapply(etfs.red, function(x) nrow(x))
etfs.red <- etfs.red[etf.length > 999]
#Function to calculate fractal dimension as intruduced by John Ehlers
fractal_dimension <- function(data){
data.half <- nrow(data)/2
first.half <- 1:round(data.half)
data1 <- data[first.half]
data2 <- data[!first.half]
hl1 <- (max(data1$Price) - min(data1$Price)) / data.half
hl2 <- (max(data2$Price) - min(data2$Price)) / data.half
hl <- (max(data$Price) - min(data$Price)) / nrow(data)
D <- (log(hl1 + hl2) - log(hl)) / log(2)
D
}
#Get for all ETFs the fractal dimension 'D'
fractal_dimensions <- sapply(etfs.red, function(x) fractal_dimension(x))
#First look at fractal dimensions
#table(round(fractal_dimensions, 1))
The fractal dimension can be thought of a measure of volatility for a finencial price chart. A more common measure of volatility is the Historic Volatility. Historic Volatility (\(=HV\)) has been calculated as: \[\begin{aligned} HV &= sd(R) \\ R &= ln(\frac{V_f}{V_i}) \end{aligned}\]
\(R =\) logarithmic return
\(V_i =\) price when market closed on day i
\(V_f =\) price when market closed of the next day
#Logarithmic or continuously compounded return
get_log_return <- function(x){
nextP <- c(0, x$Price[1:length(x$Price)-1])
x[, log_return := log(nextP/Price)]
}
get_abs_return <- function(x){
nextP <- c(0, x$Price[1:length(x$Price)-1])
x[, abs_return := nextP-Price]
}
etfs.red <- lapply(etfs.red, get_log_return)
etfs.red <- lapply(etfs.red, get_abs_return)
#Historic Volatility
hist_vol <- sapply(etfs.red, function(x) sd(x$log_return[-1]))
sp500 <- fread("sp500.csv", sep = ",")
sp500[, Price := apply(sp500, 1, function(x) mean(as.numeric(c(x["High"], x["Low"]))))]
sp500 <- sp500[, c("Date", "Price")]
sp500$Date <- as.Date(sp500$Date)
get_log_return(sp500)
sp500$log_return[1] <- 0
sp500.var <- var(sp500$log_return[-1])
get_beta <- function(x, sp500.var){
x$log_return[1] <- 0
#Merge to get overlaping Dates
a <- merge(x, sp500, by = "Date")
x.cov <- cov(a$log_return.x, a$log_return.y)
beta <- x.cov/sp500.var
beta
}
betas <- sapply(etfs.red, function(x) get_beta(x, sp500.var))
#Calculating sharpe ration S = mean(daily_return - daily_rf) / std(daily_return)
#daily_rf = 0%
calc_sharpe_ratio <- function(x, abs = FALSE){
if(abs == FALSE){
sharpe <- mean(x$log_return[-1])/sd(x$log_return[-1])
sharpe
} else {
sharpe <- mean(x$abs_return[-1])/sd(x$abs_return[-1])
sharpe
}
}
sharpe_ratios <- sapply(etfs.red, function(x) calc_sharpe_ratio(x))
sharpe_ratios_abs <- sapply(etfs.red, function(x) calc_sharpe_ratio(x, abs = TRUE))
volatility_measures <- data.table(etf = names(etfs.red), hist_vol, fractal_dimensions, betas, sharpe_ratios_abs, overall_returns)
volatility_measures_hist <- melt(volatility_measures[, -c("etf", "overall_returns")], variable.name = "measure")
levels(volatility_measures_hist$measure) <- c("Historic Volatilitys", "Fractal Dimensions", "Betas", "Sharpe Ratios")
ggplot(volatility_measures_hist, aes(value)) +
geom_histogram(bins = 20, col = "black", fill = "lightgrey") +
facet_wrap(~measure, scales = "free") +
labs(title = "Distrubution of volatility measurments") +
theme_minimal()
pairs.panels(volatility_measures[, -c("etf")])
#lapply(a$etf, function(x) plot( etfs.red[[ x ]]$Price , type = "l"))
g_legend <- function(a.gplot){
tmp <- ggplot_gtable(ggplot_build(a.gplot))
leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
legend <- tmp$grobs[[leg]]
return(legend)}
legend <- ggplot(volatility_measures,
aes(hist_vol, betas, color = overall_returns)) +
geom_point() +
scale_color_continuous(low = "lightgrey", high = "black")
legend <- g_legend(legend)
p1 <- ggplot(volatility_measures,
aes(hist_vol, betas, size = overall_returns, color = overall_returns)) +
geom_point(aes(alpha = 0.3), show.legend = F) +
scale_color_continuous(low = "lightgrey", high = "black") +
theme_minimal()
p2 <- ggplot(volatility_measures,
aes(hist_vol, sharpe_ratios_abs, size = overall_returns, color = overall_returns)) +
geom_point(aes(alpha = 0.3), show.legend = F) +
scale_color_continuous(low = "lightgrey", high = "black") +
theme_minimal()
p3 <- ggplot(volatility_measures,
aes(hist_vol, fractal_dimensions, size = overall_returns, color = overall_returns)) +
geom_point(aes(alpha = 0.3), show.legend = F) +
scale_color_continuous(low = "lightgrey", high = "black") +
theme_minimal()
p4 <- ggplot(volatility_measures,
aes(betas, sharpe_ratios_abs, size = overall_returns, color = overall_returns)) +
geom_point(aes(alpha = 0.3), show.legend = F) +
scale_color_continuous(low = "lightgrey", high = "black") +
theme_minimal()
p5 <- ggplot(volatility_measures,
aes(betas, fractal_dimensions, size = overall_returns, color = overall_returns)) +
geom_point(aes(alpha = 0.3), show.legend = F) +
scale_color_continuous(low = "lightgrey", high = "black") +
theme_minimal()
p6 <- ggplot(volatility_measures,
aes(sharpe_ratios_abs, fractal_dimensions, size = overall_returns, color = overall_returns)) +
geom_point(aes(alpha = 0.3), show.legend = F) +
scale_color_continuous(low = "lightgrey", high = "black") +
theme_minimal()
layout <- rbind(c(1,NA,7),c(2,4,NA),c(3,5,6))
grid.arrange(p1, p2, p3, p4, p5, p6, legend,
layout_matrix = layout,
top = "Volatility Measures with Overall Return")
In the following plots I compare the ETFs with the lowest and highest values for all the calculated volatility measures. Additionally to the Price chart I plotted the daily price fluctuation. This is simply the change of the Price from one day to the next.
price_fluc <- function(price.chart){
next.price <- c(NA, price.chart[1:nrow(price.chart)-1]$Price)
price.chart[, price_change := Price - next.price]
}
plot_price_chart <- function(etfs, variable, variable.name = "", max = TRUE){
require(magrittr)
require(plotly)
require(data.table)
if(max == TRUE){
max.chart <- etfs[[which.max(variable)]]
max_var <- variable[which.max(variable)]
max.chart <- price_fluc(max.chart)
p <- max.chart %>% plot_ly(x = ~Date, y = ~Price) %>% add_lines()
p1 <- max.chart %>%
plot_ly(x = ~Date, y = ~price_change, type = "bar") %>%
layout(yaxis = list(title = "Price Change"))
subplot(p, p1, nrows = 2, shareX = T, titleY = T) %>%
layout(title=paste("Price chart of ",
names(max_var) ,
" (",
variable.name,
" = ",
round(max_var, 4), ")",
sep = ""), showlegend = F)
} else {
min.chart <- etfs[[which.min(variable)]]
min_var <- variable[which.min(variable)]
min.chart <- price_fluc(min.chart)
p <- min.chart %>% plot_ly(x = ~Date, y = ~Price) %>% add_lines()
p1 <- min.chart %>%
plot_ly(x = ~Date, y = ~price_change, type = "bar") %>%
layout(yaxis = list(title = "Price Change"))
subplot(p, p1, nrows = 2, shareX = T, titleY = T) %>%
layout(title=paste("Price chart of ",
names(min_var) ,
" (",
variable.name,
" = ",
round(min_var, 4), ")",
sep = ""), showlegend = F)
}}
plot_price_chart(etfs.red, hist_vol, "HV", max = T)
plot_price_chart(etfs.red, hist_vol, "HV", max = F)
plot_price_chart(etfs.red, fractal_dimensions, "D", max = T)
plot_price_chart(etfs.red, fractal_dimensions, "D", max = F)
plot_price_chart(etfs.red, betas, "Beta", max = T)
plot_price_chart(etfs.red, betas, "Beta", max = F)
plot_price_chart(etfs.red, sharpe_ratios_abs, "Sharpe Ratio", max = T)
plot_price_chart(etfs.red, sharpe_ratios_abs, "Sharpe Ratio", max = F)